import sys
import threading


class KThread(threading.Thread):
    def __init__(self, *args, **keywords):
        threading.Thread.__init__(self, *args, **keywords)
        self.killed = False

    def start(self):
        self.__run_backup = self.run
        self.run = self.__run
        threading.Thread.start(self)

    def __run(self):
        sys.settrace(self.globaltrace)
        self.__run_backup()
        self.run = self.__run_backup

    def globaltrace(self, frame, why, arg):
        if why == "call":
            return self.localtrace
        else:
            return None

    def localtrace(self, frame, why, arg):
        if self.killed:
            if why == "line":
                raise SystemExit()
        return self.localtrace

    def kill(self):
        self.killed = True


_threads: dict[str, threading.Thread] = {}

def _handle_tag_type(tag) -> str:
    return str(tag)

def get_thread(tag) -> KThread | None:
    tag = _handle_tag_type(tag)
    return _threads.get(tag)

def is_thread_alive(tag) -> bool:
    tag = _handle_tag_type(tag)
    p = get_thread(tag)
    if p is not None:
        # print(tag, p.is_alive())
        return p.is_alive()
    return False

def start_thread(tag, target, args=(), daemon=False):
    tag = _handle_tag_type(tag)
    if is_thread_alive(tag):
        return
    p = KThread(target=target, args=args)
    p.daemon = daemon
    _threads[tag] = p
    p.start()

def stop_thread(tag):
    tag = _handle_tag_type(tag)
    if is_thread_alive(tag):
        get_thread(tag).kill()
    _threads.setdefault(tag)
